#include "synth.hpp"
#include <fstream>
#include <iostream>

#include <assert.h>
#include <math.h>
#include <sekai/common.h>
#include <sekai/mfcc.h>
#include <sndfile.h>
#include <stdlib.h> /* srand, rand */

#define TWOPI (2 * M_PI)
#define IMP_LENGTH_MAX (1024 * 8)

using namespace std;

extern std::string basedir;

double midi_freq(float m) {
  /* converts a MIDI note number to a frequency
     <http://en.wikipedia.org/wiki/MIDI_Tuning_Standard> */
  return 440.0 * pow(2, (double)(m - 69.0) / 12.0);
}

void window(float *wav, int length) {
  int i;
  for (i = 0; i < length; i++) {
    wav[i] =
        wav[i] *
        (0.5 - 0.5 * cos(2.0 * M_PI * (float)(i + 1) / ((float)(length + 1))));
  }
}

Synth::Synth() {
  for (int i = 0; i < MAX_POLYPHONY; i++) {
    key[i] = 0;
    freq[i] = 0;
    //FIXME:voice[i] = new VoiceSampler(olabuffer_length);
  }

  impulse_response = new float[IMP_LENGTH_MAX];
  impulse_response_a = new float[IMP_LENGTH_MAX];
  impulse_response_b = new float[IMP_LENGTH_MAX];

  json.readFile("test.json");
}

void Synth::init(int samplerate, int buffer_size) {
  this->samplerate = samplerate;
  this->buffer_size = buffer_size;
}

Synth::~Synth() {}

void Synth::noteOn(int notenum, int velocity) {
  for (int i = 0; i < MAX_POLYPHONY; i++) {
    if (freq[i] == 0) {
      freq[i] = midi_freq(notenum + bend);
      key[i] = notenum;
      if (i > 0)
        voice[i]->setInputPositionSamples(voice[0]->inputPositionSamples());
      active_keys++;
      break;
    }
  }
}
void Synth::noteOff(int notenum) {
  for (int i = 0; i < MAX_POLYPHONY; i++) {
    if (key[i] == notenum) {
      freq[i] = 0;
      key[i] = 0;
      voice[i]->reset();
      voice[i]->setInputPositionSamples(0);
      active_keys--;
      break;
    }
  }
}

void Synth::controllerEvent(int a1, int a2) {
  if (a1 == 1)
    speed = 128 - a2;
}

void Synth::pitchBend(int a1, int a2) {
  float fract = 1.0 * (a2 * 128 + a1 - 8 * 1024) / (8 * 1024);
  bend = fract;
  for (int i = 0; i < MAX_POLYPHONY; i++) {
    if (freq[i])
      freq[i] = midi_freq(key[i] + bend);
  }
}

//FIXME
void Synth::fill(float *buffer, int size) {
  for (int i = 0; i < size; i++)
    buffer[i] = 0;

  if (active_keys == 0)
    return;

  float buffer_tmp[size];

  float y_end = json.getLength();

  float sc = speed / 64.0 * 10;

  float x[2] = {0, sc};
  float y[2] = {0, y_end};
  int n = 2;

  for (int i = 0; i < MAX_POLYPHONY; i++) {
    if (key[i]) {
      float T0 = (samplerate / freq[i]);

      float currentIndex = voice[i]->inputPositionSamples();
      float currentTime = currentIndex / samplerate;

      float x_end = x[n - 1];
      if (currentTime >= x_end)
        currentTime = x_end - 0.001;

      float base_f0 = json.getPitch();
      int mbr_period = (int)((samplerate / base_f0) + 0.5);

      assert(mbr_period < IMP_LENGTH_MAX);

      float pos = interp_linear(x, y, n, currentTime);
      memset(impulse_response_a, 0, sizeof(float) * mbr_period * 2);
      memset(impulse_response_b, 0, sizeof(float) * mbr_period * 2);
      float interp = 0.5;
      bool result = json.copyIn(impulse_response_a, impulse_response_b, &interp,
                                mbr_period * 2, pos);
      for (int i = 0; i < mbr_period * 2; i++) {
        impulse_response[i] = impulse_response_a[i] * (1 - interp) +
                              impulse_response_b[i] * interp;
      }
      window(buffer, mbr_period * 2);

      // voice[i]->debugPos(i);
      //voice[i]->addPulses(impulse_response, mbr_period * 2, T0, size);
    }
  }

  for (int i = 0; i < MAX_POLYPHONY; i++) {
    if (key[i]) {
      //FIXME
      //voice[i]->readData(buffer_tmp, size);
      for (int j = 0; j < size; j++)
        buffer[j] += buffer_tmp[j];
    }
  }

  for (int i = 0; i < size; i++)
    buffer[i] /= active_keys;
}
